﻿/* dk2Repository - Copyright (C) 2011 Daniel Kuczewski http://www.dk2.de/

 * This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public
License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version.
This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU Lesser General Public License for more details.
You should have received a copy of the GNU Lesser General Public License along with this library; if not, write to the Free Software
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301  USA */

using System;
using System.Collections.Generic;
using dk2Database;
using dk2Repository.Commands;
using dk2Repository.Interfaces;

namespace dk2Repository
{

    public partial class Repository<T>  where T : class, new()
    {

        private static Database _database;

        public static Database Database
        {
            get
            {
                if (_database != null && _database.IsDisposed)
                    _database = null;

                return _database ?? (_database = Databases.Default);
            }
            private set { _database = value; }

        }


        private static readonly CommandFactory<T> _cmdFactory;
        private static readonly IMapping _mapping;

        static Repository()
        {
            var dba = (DatabaseAttribute)Attribute.GetCustomAttribute(typeof(T), typeof(DatabaseAttribute));
            if (dba != null && !string.IsNullOrEmpty(dba.DatabaseName))
                Database = Databases.Get(dba.DatabaseName);

            _mapping = MappingFactory.GetMappingInfo<T>();

            var sqlBuilder = QueryBuilders.GetBuilder(Database.GetType());
            if (sqlBuilder == null)
                throw new Exception("There's no SqlBuilder for " + Database.GetType().Name);


            _cmdFactory = new CommandFactory<T>(sqlBuilder, _mapping);

        }





        public static IList<T> Query(string sql, params object[] parameters)
        {
            var cmd = new MappedQueryCommand<T>(sql, parameters);
            cmd.ConnectionFactory = _database;
            cmd.Execute();
            return cmd.Result;
        }

        public static IList<T> Query(Func<object[], T, T> handleItem, string sql, params object[] parameters)
        {
            var cmd = new LambdaMappedQueryCommand<T>(handleItem, sql, parameters);
            cmd.ConnectionFactory = _database;
            cmd.Execute();
            return cmd.Result;

        }

        public static void CreateTableIfNotExists()
        {
            var cmd = _cmdFactory.CreateCreateTable(true);
            cmd.ConnectionFactory = _database;
            cmd.Execute();
        }

        public static void CreateTable()
        {
            var cmd = _cmdFactory.CreateCreateTable(false);
            cmd.ConnectionFactory = _database;
            cmd.Execute();

        }

        public static IList<T> SelectAll()
        {
            var cmd = _cmdFactory.CreateFindAll();
            cmd.ConnectionFactory = _database;
            cmd.Execute();
            return cmd.Result;
        }

        public static IList<T> SelectAll(bool includereferences)
        {
            return Select(includereferences, "", "");
        }

        public static IList<T> Select(string where, string order, params object[] parameters)
        {
            return Select(false, where, order, parameters);
        }

        public static IList<T> Select(bool includereferences, string where, string order, params object[] parameters)
        {
            var cmd = _cmdFactory.CreateFindAll(where, order, includereferences, parameters);
            cmd.ConnectionFactory = _database;
            cmd.Execute();
            return cmd.Result;
        }

        public static T SelectById(object id)
        {
            return SelectById(id, false);
        }

        public static T SelectById(object id, bool includereferences)
        {
            if (_mapping.PkType == EnumPkType.None || _mapping.PkType == EnumPkType.Multiple)
                throw new NotSupportedException("FindById method needs one PrimaryKey");
            
            var cmd = _cmdFactory.CreateFindById(id, includereferences);
            cmd.ConnectionFactory = _database;
            cmd.Execute();

            var items = cmd.Result;
            return items.Count > 0 ? items[0] : default(T);
        }

        public static bool Update(T item)
        {
            if (_mapping.PkType == EnumPkType.None)
                throw new NotSupportedException("Update method needs at least one PrimaryKey");

            var cmd = _cmdFactory.CreateUpdate(item);
            cmd.ConnectionFactory = _database;
            cmd.Execute();
            return (int)cmd.Result == 1;
        }

        public static void Insert(T item)
        {
            var cmd = _cmdFactory.CreateInsert(item);
            cmd.ConnectionFactory = _database;
            cmd.Execute();
        }

        public static bool Delete(T item)
        {
            if (_mapping.PkType == EnumPkType.None)
                throw new NotSupportedException("Delete method needs at least one PrimaryKey");

            var cmd = _cmdFactory.CreateDelete(item);
            cmd.ConnectionFactory = _database;
            cmd.Execute();

            return (int)cmd.Result == 1;
       }
    }
}